//
//  MinFlex.swift
//  soapp_Example
//
//  Created by zhu xietong on 2018/2/27.
//  Copyright © 2018年 CocoaPods. All rights reserved.
//

import Foundation

import AsyncDisplayKit



extension ASDisplayNode
{
    
    public var Spec:_Spec{
        get{
            weak var wself = self
            return _Spec.node(wself)
//            if let ws = wself
//            {
//                return _Spec.node(ws)
//            }
//            else{
//                return _Spec.node(ASDisplayNode())
//            }
        }
    }
    
    public subscript(_ specs:Any...)->_Spec{
        weak var wself = self
        if let ws = wself
        {
            let spec = SpecCreate.assin(rules: specs, element: ws)
            return spec
        }
        else{
            
            return _Spec.node(nil)
        }
    }
    
    public subscript(frame:CGRect)->ASDisplayNode
    {
        style.layoutPosition = frame.origin
        style.preferredSize = frame.size
        return self
    }
}

extension ASLayoutElement
{    
    public subscript(frame:CGRect)->ASLayoutElement
    {
        style.layoutPosition = frame.origin
        style.preferredSize = frame.size
        return self
    }
}



public enum FlexElement {
    case node(ASDisplayNode)
    case spec(ASLayoutSpec)
    
    
    var element:ASLayoutElement
    {
        switch self {
        case .node(let node):
            return node
        case .spec(let spec):
            return spec
        }
    }
    
}

public enum DigitalValue
{
    case equal(value:Double,key:String)
    case max(value:Double,key:String)
    case mini(value:Double,key:String)
}


public enum FlexValue {
    case width(DigitalValue)
    case height(DigitalValue)
    case padding(UIEdgeInsets,options:[Any])
    case direction(ASStackLayoutDirection)
    case space(Double)
    case justifyContent(ASStackLayoutJustifyContent)
    case alignItems(ASStackLayoutAlignItems)
    case alignContent(ASStackLayoutAlignContent)
    case alignSelf(ASStackLayoutAlignSelf)
    
    case grow(Double)
    case shrink(Double)
    case wrap(ASStackLayoutFlexWrap)
    case specId(String)
    
}



extension ASDisplayNode:SpecKit{
    public var __element: FlexElement {
        return FlexElement.node(self)
    }
    
    public var __nodes: [ASDisplayNode] {
        return [self]
    }
}
extension _Spec:SpecKit{
    
    public var __nodes:[ASDisplayNode]{
        get{
            return specAndNodes.1
        }
    }
    
    public var __element: FlexElement {
        return self.specAndNodes.0
    }
}

public protocol SpecKit {
    var __nodes:[ASDisplayNode]{get}
    var __element:FlexElement{get}

}


extension _Spec:ExpressibleByArrayLiteral
{
    
    public init(arrayLiteral elements: Any...) {
        var specs:[_Spec] = [_Spec]()
        for one in elements
        {
            if let __specs = one as? [_Spec]
            {
                specs = specs + __specs
            }
        }
        
        let values = elements.filter { (one) -> Bool in
            if one is [_Spec]
            {
                return false
            }
            return true
        }
        
        self = .stack(values, childs: specs)
        
    }

}


public typealias MiniRule = [Any]

var specNum = 0

public indirect enum _Spec{
    
    case wrapper(_Spec)
    case node(ASDisplayNode?)
    case stack([Any],childs:[_Spec])
    case inset([Any],UIEdgeInsets,child:_Spec)
    case over([Any],over:_Spec,back:_Spec)
    case back([Any],over:_Spec,back:_Spec)
    case center([Any],xy:FlexCenter,child:_Spec)//FlexCenter 设置x时child的width 无效,设置y时child的height无效
    case ratio([Any],ratio:CGFloat,child:_Spec)//注意大小的控制需要在在rules 中给到一个宽带或高度才能按比例显示
    case relative([Any],horizontal:ASRelativeLayoutSpecPosition,vertical:ASRelativeLayoutSpecPosition,child:_Spec)
    case absolute([Any],childs:[_Spec])
    case `if`(Bool,spec:_Spec)
    
    public func addOver(_ spec:_Spec) ->_Spec{
        return _Spec.over([], over: spec, back: self)
    }
    
    public func addBack(_ spec:_Spec) ->_Spec{
        
        return _Spec.back([], over: self, back: spec)
    }
    
    public var laySpec:ASLayoutSpec{
        get{
            let spec = self.specAndNodes.0
            switch spec {
            case .node(let node):
                return ASWrapperLayoutSpec(layoutElement: node)
            case .spec(let _spec):
                return _spec
            }
        }
    }
    
    
    public var specAndNodes:(FlexElement,[ASDisplayNode]){
        get{
            switch self {
            case .if(let need, spec: let spec):
                if(need){
                    return spec.specAndNodes
                }else{
                    return (FlexElement.node(ASDisplayNode()),[])
                }
                
            case .node(let node):
                let _node = node ?? ASDisplayNode()
                return (.node(_node),[_node])
            case .stack(let rule, childs: let specs):
                var childrens = [ASLayoutElement]()
                var nodes = [ASDisplayNode]()
//                for one in specs
//                {
//                    childrens.append(one.__element.element)
//                    nodes = nodes + one.__nodes
//                }
                for one in specs
                {
                    switch one {
                    case .if(let need, spec: let _one):
                        if(need){
                            childrens.append(_one.__element.element)
                            nodes = nodes + _one.__nodes
                        }else{
//                            print("-----iiiiii---",one)
                        }
                    default:
                        childrens.append(one.__element.element)
                        nodes = nodes + one.__nodes
                    }
                    
                 
                }
                return (.spec(SpecCreate.newStack(rules: rule, childs: childrens)),nodes)
            case .absolute(_,childs: let specs):
                var nodes = [ASDisplayNode]()

                var childrens = [ASLayoutElement]()
                for one in specs
                {
                    childrens.append(one.__element.element)
                    nodes = nodes + one.__nodes
                }
                return (.spec(ASAbsoluteLayoutSpec(children: childrens)),nodes)
            case .ratio(let rule,ratio: let ratio, child: let child):
                
                let __spec = ASRatioLayoutSpec(ratio: ratio, child: child.__element.element)
                let spec = SpecCreate.assin(rules: rule, spec: __spec)
                return (.spec(spec),child.__nodes)
                
            case .back(let rule,over: let over, back: let back):
                
                let nodes =  back.__nodes + over.__nodes
                let __spec = ASBackgroundLayoutSpec(child: over.__element.element, background: back.__element.element)
                let spec = SpecCreate.assin(rules: rule, spec: __spec)
                return (.spec(spec),nodes)
            case .center(let rule, xy: let xyOption, child: let child):
                
                let __spec = ASCenterLayoutSpec(centeringOptions: xyOption.value, sizingOptions: [], child: child.__element.element)
                let spec = SpecCreate.assin(rules: rule, spec: __spec)

                return (.spec(spec),child.__nodes)

            case .wrapper(let node):
                return (.spec(ASWrapperLayoutSpec(layoutElement: node.__element.element)),node.__nodes)
            case .inset(let rule, let inset, let child):
                let __spec = ASInsetLayoutSpec(insets: inset, child: child.__element.element)
                let spec = SpecCreate.assin(rules: rule, spec: __spec)
                return (.spec(spec),child.__nodes)
            case .over(let rule,let over, let back):
                let __spec = ASOverlayLayoutSpec(child: back.__element.element, overlay: over.__element.element)
                let spec = SpecCreate.assin(rules: rule, spec: __spec)
                
                let nodes =  back.__nodes + over.__nodes
                return (.spec(spec),nodes)
            case .relative(let rule, let horizontal, let vertical, let child):
                let __spec = ASRelativeLayoutSpec(horizontalPosition: horizontal, verticalPosition: vertical, sizingOption: [], child: child.__element.element)
                let spec = SpecCreate.assin(rules: rule, spec: __spec)
                
                return (.spec(spec),child.__nodes)
            }
            
        }
    }
    
    
//    deinit{
//        print("==----sss===========|\(specNum)")
//    }
    
}

//struct Eee {
//
//    var spec:_Spec = .node(ASDisplayNode())
//    static func stack(rules:[Any]? = nil,childs:Eee...)->Eee{
//        let _rules = rules ?? [Any]()
//        let specs = childs.map { (one) -> _Spec in
//            one.spec
//        }
//        let spec  = _Spec.stack(_rules, childs: specs)
//        return Eee(spec: spec)
//    }
//
//    static func inset(inset:UIEdgeInsets = .zero,child:Eee)->Eee{
//        let spec  = _Spec.inset(inset, child:child.spec)
//        return Eee(spec: spec)
//    }
//}

